package medusa

import (
	"encoding/binary"
	//"fmt"
	"io"
	"log"
	"sync"
)

type IPacket interface {
	Show()
	Clear()
	PacketSize() int32
	DataSize() int32
	Check() bool
}

const (
	PACKET_HEAD_LEN = 34
)

type Packet struct {
	ByteBuffer
	M                byte //1:+ |2:- |3:* |4/
	A, B             int16
	C                uint16
	Length           int32
	Des              uint16
	uuid             int32
	is_space_msg     bool
	except_uuid      int32
	notify_or_result int32
	timestamp        int64

	head ByteBuffer

	//sync
	//wg   OnceSet
	once       sync.Once
	write_head func()
}

func NewRecvPacket(m byte, a, b int16, c uint16,
	length int32, msgcode uint16,
	uuid int32, is_space_msg bool, except_uuid int32, notify_or_result int32,
	timestamp int64) *Packet {

	pkg := new(Packet)
	pkg.M = m
	pkg.A = a
	pkg.B = b
	pkg.C = c
	pkg.Length = length
	pkg.Des = msgcode
	pkg.uuid = uuid
	pkg.is_space_msg = is_space_msg
	pkg.except_uuid = except_uuid
	pkg.notify_or_result = notify_or_result
	pkg.timestamp = timestamp

	pkg.init()

	return pkg
}

func NewPacket(msgcode int) *Packet {
	pkg := new(Packet)
	pkg.M = 4
	pkg.A = 7
	pkg.B = 3
	pkg.C = 2
	pkg.Length = PACKET_HEAD_LEN
	pkg.Des = (uint16)(msgcode)
	pkg.uuid = 0
	pkg.is_space_msg = false
	pkg.except_uuid = 0
	pkg.notify_or_result = 0
	pkg.timestamp = 0

	pkg.init()

	return pkg
}

func (self *Packet) init() {
	self.write_head = func() {
		self.head.Clear()

		self.head.WriteByte(self.M)             //1
		self.head.WriteInt16LE(self.A)            //3
		self.head.WriteInt16LE(self.B)            //5
		self.head.WriteUint16LE(self.C)           //7
		self.head.WriteInt32LE(self.DataSize32()) //11
		self.head.WriteUint16LE(self.Des)         //13
		self.head.WriteInt32LE(self.uuid)         //17
		if self.is_space_msg {                  //18
			self.head.WriteByte(1)
		} else {
			self.head.WriteByte(0)
		}
		self.head.WriteInt32LE(self.except_uuid)      //22
		self.head.WriteInt32LE(self.notify_or_result) //26
		self.head.WriteInt64LE(self.timestamp)        //34
	}
}

func (self *Packet) Trace(pre string) {

	log.Printf("[%v]:\nM:%v|A:%v|B:%v|C:%v|Len:%v|MSG:%v|session:%v|is_space:%v|except:%v|notify_or_result:%v|timestamp:%v\n",
		pre,
		self.M, self.A, self.B, self.C,
		self.Length, self.Des,
		self.uuid, self.is_space_msg, self.except_uuid, self.notify_or_result,
		self.timestamp)
}

func (self *Packet) PacketSize() int {
	return (int)(self.Length)
}

func (self *Packet) DataSize() int {
	return PACKET_HEAD_LEN + self.Len()
}

func (self *Packet) DataSize32() int32 {
	return (int32)(PACKET_HEAD_LEN + self.Len())
}

func (self *Packet) Check() bool {
	var ret bool = false
	switch self.M {
	case 1:
		ret = self.C == (uint16)(self.A+self.B)
		break
	case 2:
		ret = self.C == (uint16)(self.A-self.B)
		break
	case 3:
		ret = self.C == (uint16)(self.A*self.B)
		break
	case 4:
		ret = self.C == (uint16)(self.A/self.B)
		break
	default:
		break
	}

	return ret
}

func (self *Packet) SetSessionMode(pid int32) {
	self.is_space_msg = false
	self.uuid = pid
	self.except_uuid = 0
}

func (self *Packet) SetSpaceMode(pid int32, except_uuid int32) {
	self.is_space_msg = true
	self.uuid = pid
	self.except_uuid = except_uuid
}

func (self *Packet) GetSessionUUID() int32 {
	return self.uuid
}
func (self *Packet) GetSpaceUUID() int32 {
	return self.uuid
}

func (self *Packet) GetExceptUUID() int32 {
	return self.except_uuid
}

func (self *Packet) IsSpacePacket() bool {
	return self.is_space_msg
}

/*
func (self *Packet) write(w io.Writer) error {

	binary.Write(w, binary.LittleEndian, self.M)
	binary.Write(w, binary.LittleEndian, self.A)
	binary.Write(w, binary.LittleEndian, self.B)
	binary.Write(w, binary.LittleEndian, self.C)
	binary.Write(w, binary.LittleEndian, self.Length)
	binary.Write(w, binary.LittleEndian, self.pid)
	binary.Write(w, binary.LittleEndian, self.Des)
	binary.Write(w, binary.LittleEndian, self.Bytes())
	return nil
}*/

func (self *Packet) flush_head() {
	self.once.Do(self.write_head)
}

func (self *Packet) Write2IO(w io.Writer) error {
	/*
		*(*int32)(unsafe.Pointer(&(self.Bytes()[0]))) = (int32)(self.Len())
		*(*uint16)(unsafe.Pointer(&(self.Bytes()[4]))) = self.Des
		*(*int32)(unsafe.Pointer(&(self.Bytes()[6]))) = (int32)(self.session_or_space_id)

		if self.is_space_msg {
			self.Bytes()[10] = 1
		} else {
			self.Bytes()[10] = 0
		}
		self.Bytes()[11] = self.Flag
		err := binary.Write(w, binary.LittleEndian, self.Bytes())
		if err != nil {
			return err
		}*/
	/*
			self.head.Bytes()[0] = self.M
			*(*int16)(unsafe.Pointer(&(self.head.Bytes()[1]))) = self.A
			*(*int16)(unsafe.Pointer(&(self.head.Bytes()[3]))) = self.B
			*(*uint16)(unsafe.Pointer(&(self.head.Bytes()[5]))) = self.C
			*(*int32)(unsafe.Pointer(&(self.head.Bytes()[7]))) = (int32)(self.DataSize())
			*(*uint16)(unsafe.Pointer(&(self.head.Bytes()[11]))) = self.Des
			*(*int32)(unsafe.Pointer(&(self.head.Bytes()[13]))) = self.uuid
			if self.is_space_msg {
				self.head.Bytes()[17] = 1
			} else {
				self.head.Bytes()[17] = 0
			}
			*(*int32)(unsafe.Pointer(&(self.head.Bytes()[18]))) = self.except_uuid
			*(*int32)(unsafe.Pointer(&(self.head.Bytes()[22]))) = self.notify_or_result
		    *(*int64)(unsafe.Pointer(&(self.head.Bytes()[26]))) = self.timestamp
	*/
	/*
	   self.wg.Set(func() {
	       self.flush_head()
	   })*/
	self.flush_head()

	err := binary.Write(w, binary.LittleEndian, self.head.Bytes())
	if err == nil {
		err = binary.Write(w, binary.LittleEndian, self.Bytes())
	}
	return err
}
